﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ArduinoSketch.Core
{
    public delegate void SetText();

    public partial class SerialMonitor : BaseFormContent, ITerminal
    {
        private int _sendCount;
        private int _receiveCount;


        private string _strReceive = "";
        private byte _bytReceive = 0;
        private bool _receiveASCII = true;
        private bool _sendImmediate = true;
        private string _queuedString = "";

        private SetText _setTextCallback;

        public SerialMonitor()
        {
            InitializeComponent();
            this.ShowSetting();
        }

        private void sendChar(char c)
        {
            char[] data = new Char[1];
            data[0] = c;
            try
            {
                serialPort.Write(data, 0, 1);
                _sendCount++;
                lbSendCount.Text = _sendCount.ToString(); 
            }
            catch
            {
                MessageBox.Show("Error: sendChar - failed to send.\nIs the port open?");
            }
        }

        private void sendByte(byte b)
        {
            byte[] data = new byte[1];
            data[0] = b;
            try
            {
                serialPort.Write(data, 0, 1);
                _sendCount++;
                lbSendCount.Text = _sendCount.ToString();  
            }
            catch
            {
                MessageBox.Show("Error: sendChar - failed to send.\nIs the port open?");
            }
        }

        public bool IsPortOpen()
        {
            return serialPort.IsOpen;
        }

        public void ClearReceiveText()
        {
            richTextBoxReceive.Clear();
        }

        public void SetTextCallblack()
        {
            _receiveCount += _strReceive.Length;
            lbReceiveCount.Text = _receiveCount.ToString();

            if (_receiveASCII)
            {
                richTextBoxReceive.AppendText(_strReceive.ToString());
            }
            else // ReceiveHEX
            {
                if (_bytReceive > 15)
                {
                    richTextBoxReceive.AppendText("0x" + _bytReceive.ToString("X") + ",");
                }
                else 
                    richTextBoxReceive.AppendText("0x0" + _bytReceive.ToString("X") + ",");
            }
            richTextBoxReceive.ScrollToCaret();
        }

        private void SendASCIIString(string str)
        {
            char[] c = str.ToCharArray();

            for (int i = 0; i < c.GetLength(0); i++)
            {
                sendChar(c[i]);
            }
        }

        private void richTextBoxSend_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (_sendImmediate)
            {
                sendChar(e.KeyChar); 
            }
            else
            {
                _queuedString += e.KeyChar;
            }
        }

        private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            // instantiate the delegate to be invoked by this thread
            _setTextCallback = new SetText(SetTextCallblack);
            try
            {
                if (_receiveASCII)
                {
                    _strReceive = serialPort.ReadExisting();
                }
                else
                {
                    _bytReceive = (byte)serialPort.ReadByte();// .ReadExisting();
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Error - port_DataReceived Exception: " + ex);
            }

            // invoke the delegate in the MainForm thread
            this.Invoke(_setTextCallback);
        }

        private void btnSendQueue_Click(object sender, EventArgs e)
        {
            richTextBoxSend.Text += "\n Sent QueuedString: \n" + _queuedString + "\n";
            SendASCIIString(_queuedString);
            _queuedString = "";
        }

        #region Iterminal Members

        private PortSettings _portSettings;

        public void PortPropertiesSetting()
        {
            // Make sure the port isn't already open
            if (serialPort.IsOpen)
            {
                MessageBox.Show("The port must be closed before changing the settings.");
                return;
            }
            else
            {
                _portSettings = new PortSettings();
                if (_portSettings.ShowDialog() == DialogResult.OK)
                {
                    if (_portSettings.selectedPort != "")
                    {
                        // set the serial port to the new settings
                        serialPort.PortName = _portSettings.selectedPort;
                        serialPort.BaudRate = _portSettings.selectedBaudrate;
                        serialPort.DataBits = _portSettings.selectedDataBits;
                        serialPort.Parity = _portSettings.selectedParity;
                        serialPort.StopBits = _portSettings.selectedStopBits;

                        this.CloaePort();
                        this.OpenPort();
                    }
                    else
                    {
                        MessageBox.Show("Error: Settings form returned with no COM port selected.");
                        return; // bail out
                    }
                } 
                _portSettings.Dispose();
                GC.SuppressFinalize(_portSettings);
                _portSettings = null;
            }        
        }

        public void OpenPort()
        {
            try
            {
                if (!serialPort.IsOpen)
                {
                    serialPort.Open();
                    serialPort.DtrEnable = true;
                    System.Threading.Thread.Sleep(10);
                    serialPort.DtrEnable = false;

                    ShowSetting();
                } 
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Error - OpenPort Exception: " + ex);
            }
        }

        private void ShowSetting()
        {
            this.lbSetting.Text = String.Format("Port Properties [{0} {1},{2},{3},{4}-{5}]",
                new object[] { 
                            serialPort.PortName, 
                            serialPort.BaudRate, 
                            serialPort.DataBits, 
                            serialPort.Parity, 
                            serialPort.StopBits,
                            serialPort.IsOpen ? "Port is open" : "Port is close"
                });
        }

        public void CloaePort()
        {           
            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Close();

                    this.ShowSetting();
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Error - CloaePort Exception: " + ex);
            }
        }

        #endregion

        private void SerialMonitor_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.CloaePort();
        }

        private void radHex_CheckedChanged(object sender, EventArgs e)
        {
            _receiveASCII = radAscii.Checked;
        }

        private void radAscii_CheckedChanged(object sender, EventArgs e)
        {
            _receiveASCII = radAscii.Checked;
        }

        private void radQueued_CheckedChanged(object sender, EventArgs e)
        {
            _sendImmediate = radImmediate.Checked;
        }

        private void radImmediate_CheckedChanged(object sender, EventArgs e)
        {
            _sendImmediate = radImmediate.Checked;
        }
    }
}
